use std::{fs, path::Path};

use log::{info, warn};
use serde::Serialize;
use serde_json::Value;

use crate::{
    history, History, HistoryEncoder, Message, MessageRole, TalkTiveConfig, TalktiveKenerl,
    TalktiveKenerlAns, TalktiveKenerlConfig, ToolCollection, ToolCollectionBuilder, ToolParm,
};

pub struct TalkTiveBuilder<K>
where
    K: TalktiveKenerl,
{
    kenerl: K,
    config: TalkTiveConfig,
    history: Option<History<K::HistoryEncoder>>,
    tools: Option<ToolCollectionBuilder>,
}

pub struct TalkTive<K>
where
    K: TalktiveKenerl,
{
    kenerl: K,
    pub history: History<K::HistoryEncoder>,
    pub talktive_config: TalkTiveConfig,
    tools: Option<ToolCollection<K::ToolSigEncoder>>,
}

#[derive(Debug, Clone)]
pub struct TalkTiveAns<H>
where
    H: HistoryEncoder,
{
    pub message: Message<H::MessageRole>,
    pub history: Option<History<H>>,
}

unsafe impl<K> Sync for TalkTive<K> where K: TalktiveKenerl {}
impl<K> TalkTiveBuilder<K>
where
    K: TalktiveKenerl,
{
    pub fn new_load_kenerl_config<P>(
        path: P,
        talktive_config: TalkTiveConfig,
    ) -> anyhow::Result<Self>
    where
        P: AsRef<Path>,
    {
        let path = path.as_ref();
        let kenerl_config = if path.exists() {
            let rdr = fs::File::open(path)?;
            info!("Loading talktive kenerl config...");
            let kenerl_config: K::KenerlConfig = serde_yaml::from_reader(rdr)?;
            kenerl_config
        } else {
            let kenerl_config = K::KenerlConfig::default();
            let mut wtr = fs::File::create(path)?;
            serde_yaml::to_writer(wtr, &kenerl_config)?;
            info!(
                "Talktive kenerl config not found.Generating default at {:?} ...",
                path
            );
            kenerl_config
        };
        Self::new(&kenerl_config, talktive_config)
    }
    pub fn new(
        kenerl_config: &K::KenerlConfig,
        talktive_config: TalkTiveConfig,
    ) -> anyhow::Result<Self> {
        Ok(Self {
            kenerl: K::build(kenerl_config)?,
            history: None,
            tools: None,
            config: talktive_config,
        })
    }
    pub fn with_history(mut self, history: History<K::HistoryEncoder>) -> Self {
        self.history = Some(history);
        self
    }
    pub fn with_tools(mut self, tools: ToolCollectionBuilder) -> Self {
        self.tools = Some(tools);
        self
    }
    pub fn build(self) -> TalkTive<K> {
        let tools = if let Some(tools) = self.tools {
            Some(tools.build::<K::ToolSigEncoder>())
        } else {
            None
        };
        TalkTive {
            kenerl: self.kenerl,
            history: self.history.unwrap_or(History::new()),
            talktive_config: self.config,
            tools,
        }
    }
}

impl<K> TalkTive<K>
where
    K: TalktiveKenerl,
{
    pub async fn generate(
        &mut self,
        message: Message<<K::HistoryEncoder as HistoryEncoder>::MessageRole>,
    ) -> anyhow::Result<Vec<TalkTiveAns<K::HistoryEncoder>>> {
        self.history.push_merge(message)?;
        self.generate_args(Some(self.history.clone()), self.tools.clone())
            .await
    }
    #[async_recursion::async_recursion(?Send)]
    pub async fn generate_args(
        &self,
        history: Option<History<K::HistoryEncoder>>,
        tools: Option<ToolCollection<K::ToolSigEncoder>>,
    ) -> anyhow::Result<Vec<TalkTiveAns<K::HistoryEncoder>>> {
        let history = if let Some(his) = &history {
            Some(his.get_histry(true))
        } else {
            None
        };
        let tools_sig = if let Some(tools) = &tools {
            Some(tools.sig.clone())
        } else {
            None
        };
        let ans = self.kenerl.send(history, tools_sig).await?;
        self.center(ans, tools).await
    }
    #[async_recursion::async_recursion(?Send)]
    async fn center(
        &self,
        ans: Vec<TalktiveKenerlAns<<K::HistoryEncoder as HistoryEncoder>::MessageRole>>,
        tools: Option<ToolCollection<K::ToolSigEncoder>>,
    ) -> anyhow::Result<Vec<TalkTiveAns<K::HistoryEncoder>>> {
        let mut res = vec![];
        for ans_item in ans {
            res.append(&mut self.center_single(ans_item, tools.clone()).await?)
        }
        Ok(res)
    }
    #[async_recursion::async_recursion(?Send)]
    async fn center_single(
        &self,
        ans: TalktiveKenerlAns<<K::HistoryEncoder as HistoryEncoder>::MessageRole>,
        tools: Option<ToolCollection<K::ToolSigEncoder>>,
    ) -> anyhow::Result<Vec<TalkTiveAns<K::HistoryEncoder>>> {
        match ans {
            TalktiveKenerlAns::Message(msg) => Ok(vec![TalkTiveAns {
                message: msg,
                history: None,
            }]),
            TalktiveKenerlAns::ToolCall(toolcall) => {
                let tools = if let Some(tools) = tools {
                    tools
                } else {
                    warn!("Unexpected function call.Tools is not enabled.");
                    return Ok(vec![]);
                };
                let mut his = self.history.clone();
                for tool in toolcall {
                    let call = tools.run(tool.name(), tool.clone()).await?;
                    if call.is_ok() {
                        if let Some(s) = call.content {
                            his.push(Message::<<K::HistoryEncoder as HistoryEncoder>::MessageRole>::from_parts(<<K::HistoryEncoder as HistoryEncoder>::MessageRole as MessageRole>::observation(), s));
                        }
                    }
                }
                self.generate_args(Some(his), Some(tools)).await
            }
        }
    }
}
